package cn.tedu.dimanage.user.webapi.service;

import cn.tedu.dimanage.common.ex.ServiceException;
import cn.tedu.dimanage.common.web.State;
import cn.tedu.dimanage.pojo.dto.UserAddNewDTO;
import cn.tedu.dimanage.pojo.dto.UserLoginDTO;
import cn.tedu.dimanage.pojo.dto.UserUpdateDTO;
import cn.tedu.dimanage.pojo.entity.User;
import cn.tedu.dimanage.pojo.vo.UserDetailVO;
import cn.tedu.dimanage.pojo.vo.UserSimpleVO;
import cn.tedu.dimanage.user.service.IUserService;
import cn.tedu.dimanage.user.webapi.mapper.UserMapper;
import cn.tedu.dimanage.user.webapi.security.JwtConst;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * @Classname: UserServiceImpl
 * @Author: bromide
 * @CreateTime: 2022/6/10--11:50
 * @Version: V1.0
 * @Description:
 */
@Slf4j
@Service
//@DubboService
public class UserServiceImpl implements IUserService {

    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * JWT数据的密钥
     */
    @Value("${dimanage.jwt.secret-key}")
    private String secretKey;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

//    @Autowired
//    private PasswordEncoder passwordEncoder;

    @Override
    // todo 利用SpringSecurity来实现加密
    public void addNew(UserAddNewDTO userAddNewDTO) {
        /**
         * 检查用户名是否重复
         */
        // 通过参数获取用户名
        String username = userAddNewDTO.getUsername();
        // 调用userMapper的 getByUsername(String username) 方法执行查询
        UserSimpleVO queryResultByUsername = userMapper.getByUsername(username);
        // 判断查询结果是否为null
        if (queryResultByUsername!=null){
            //  否:表示用户名已经被暂用,则判处UsernameDuplicateException
            log.error("此账号已占用,将抛出异常");
            throw new ServiceException(State.ERR_USER_NAME_DUPLICATE,"添加管理员失败,用户名(" + username + ")已存在");
        }

        /**
         * 检查手机号是否重复
         */
        // 通过参数获取用户名
        String phone = userAddNewDTO.getPhone();
        // 调用userMapper的 getByUsername(String username) 方法执行查询
        UserSimpleVO queryResultByPhone = userMapper.getByPhone(phone);
        // 判断查询结果是否为null
        if (queryResultByPhone!=null){
            //  否:表示用户名已经被暂用,则判处UsernameDuplicateException
            log.error("此账号已占用,将抛出异常");
            throw new ServiceException(State.ERR_USER_PHONE_DUPLICATE,"添加管理员失败,手机号(" + phone + ")已存在");
        }

        /**
         * 检查邮箱是否重复
         */
        // 通过参数获取用户名
        String email = userAddNewDTO.getEmail();
        // 调用userMapper的 getByUsername(String username) 方法执行查询
        UserSimpleVO queryResultByEmail = userMapper.getByEmail(email);
        // 判断查询结果是否为null
        if (queryResultByEmail!=null){
            //  否:表示用户名已经被暂用,则判处UsernameDuplicateException
            log.error("此账号已占用,将抛出异常");
            throw new ServiceException(State.ERR_USER_EMAIL_DUPLICATE,"添加管理员失败,邮箱(" + email + ")已存在");
        }
        log.trace("此账号信息均未被占用,准备向表中写入此次添加的管理员信息");

        //  是:表示用户名未被占用,则执行插入数据  ( 无需写 )
        // 通过参数获取原密码
        String password = userAddNewDTO.getPassword();
        log.debug("原密码:" + password);
        // 通过加密方式,得到加密后的密码encodedPassword
        String encodedPassword = passwordEncoder.encode(password);
        log.debug("加密后密码:" + encodedPassword);
        // 创建当前时间对象now > LocalDateTime.now()
        LocalDateTime now = LocalDateTime.now();
        // 创建user对象
        User user = new User();
        // 补全user对象的属性值  通过userAddNewDTO获取部分参数
        user.setRoleId(3L);
        user.setUsername(username);
        user.setPassword(encodedPassword);
        user.setName(userAddNewDTO.getName());
        user.setIdNumber(userAddNewDTO.getIdNumber());
        user.setAge(userAddNewDTO.getAge());
        user.setPhone(userAddNewDTO.getPhone());
        user.setEmail(userAddNewDTO.getEmail());
        user.setAvatar(userAddNewDTO.getAvatar());
        // 以上这些从一个对象中把属性赋到另一个对象中,还可以使用:  (需要两个类中的属性名完全一致)
        // BeanUtils.copyProperties(userAddNewDTO,user);

        // 补全user对象的属性值  isEnable > 1
        user.setIsEnable(1);
        // 补全user对象的属性值  lastLoginIp > null
        user.setLastLoginIp(null);
        // 补全user对象的属性值  loginCount > 0
        user.setLoginCount(0);
        // 补全user对象的属性值  gmtLastLogin > null
        user.setGmtLastLogin(null);
        // 补全user对象的属性值  gmtCreate > now
        user.setGmtCreate(now);
        // 补全user对象的属性值  gmtModified > now
        user.setGmtModified(now);
        // 调用userMapper insert(user user) 方法插入管理员数据,获取返回值
        int row = userMapper.insert(user);
        // 判断以上返回的结果是否为1, 若不是,则抛出insertException
        if (row != 1){
            log.error("插入失败,将抛出异常");
            throw new ServiceException(State.ERR_INSERT,"添加管理员失败,服务器忙,请稍后再次尝试!");
        }
    }


    @Override
    public String login(UserLoginDTO userLoginDTO) {

        // 准备认证数据,将客户端提交的用户名密码提交过来,我们用户名和密码作为参数准备认证
        log.debug("开始处理用户登录,用户登录的信息:{}",userLoginDTO);

        Authentication authentication = new UsernamePasswordAuthenticationToken(
                userLoginDTO.getUsername(),
                userLoginDTO.getPassword()
        );
        // 调用AuthenticationManager验证用户名和密码
        // 执行认证,如果此过程没有抛出异常则表示认证通过,如果认证信息有误将抛出异常
        // 此处是Spring Security自动处理(调用UserDetailsServiceImpl)
        Authentication authenticate = authenticationManager.authenticate(authentication);

        // 如果执行到此处,则表示登陆成功
        // 生成用户数据的JWT
        // 这里面的User是Security提供的(在UserDetailsServiceImpl中返回给Security的)  因为我们自己有一个User  所以只能用全限定名的方式来声明
        org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User)authenticate.getPrincipal();

//        // 检查身份
//        if (!(userLoginDTO.getRoleId().toString()).equals(user.getAuthorities().toArray()[0].toString())){
//            throw new ServiceException(State.ERR_USER_ROLE_BAD,"身份选择错误,请重新选择");
//        }



        // todo 修改user表中的last_login_ip(最后登录IP)   login_count(登录次数)  (最后登录时间)
        // 新建一个UserUpdateDTO 为更改用户信息做准备
        UserUpdateDTO userUpdateDTO = new UserUpdateDTO();
        // 以上这些从一个对象中把属性赋到另一个对象中,还可以使用:  (需要两个类中的属性名完全一致)
        UserDetailVO detailsByUsername = userMapper.getDetailsByUsername(userLoginDTO.getUsername());
        BeanUtils.copyProperties(detailsByUsername,userUpdateDTO);
        log.debug("通过username查出来的UserDetailVO:{}",detailsByUsername);
        log.debug("赋值之后的userUpdateDTO:{}",userUpdateDTO);
        // 更改userUpdateDTO中的值
        userUpdateDTO.setLastLoginIp(userLoginDTO.getLastLoginIp());
        userUpdateDTO.setLoginCount(userUpdateDTO.getLoginCount()+1);
        userUpdateDTO.setGmtLastLogin(LocalDateTime.now());
        // 将用户表中的数据更新
        userMapper.updateUser(userUpdateDTO);


        // Claims
        // 注意: 先将roleId转换成JSON字符串再写入
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtConst.KEY_USERNAME, user.getUsername());// claims中放入用户名(UserDetailsServiceImpl处理时放入的登录信息)
        // 放进去的类型实际是GrantedAuthority
        claims.put(JwtConst.KEY_ROLEID, JSON.toJSONString(user.getAuthorities()));// claims中放入角色Id
        System.out.println("即将向JWT中写入数据=" + claims);
        // JWT的组成部分：Header（头），Payload（载荷），Signature（签名）
        String jwt = Jwts.builder()
                // Header：指定算法与当前数据类型
                // 格式为： { "alg": 算法, "typ": "jwt" }
                .setHeaderParam(Header.CONTENT_TYPE, "HS256")
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                // Payload：通常包含Claims（自定义数据）和过期时间
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000))
                // Signature：由算法和密钥（secret key）这2部分组成
                .signWith(SignatureAlgorithm.HS256, secretKey)
                // 打包生成
                .compact();

        // 返回jwt数据
        return jwt;

        /*log.debug("开始处理用户登录,用户登录的信息:{}",userLoginDTO);
        // 检查用户名是否存在
        String username = userLoginDTO.getUsername();
        UserSimpleVO userSimpleVO = userMapper.getByUsername(username);
        if (userSimpleVO == null){
            log.error("用户不存在,将抛出异常");
            // 用户选择的管理员登陆
            if (userLoginDTO.getRoleId()==1){
                throw new ServiceException(State.ERR_USER_NOT_FOUND,"管理员(" + username + ")不存在");
            }
            // 用户选择的员工登陆
            throw new ServiceException(State.ERR_USER_NOT_FOUND,"员工(" + username + ")不存在");
        }
        Integer isEnable = userSimpleVO.getIsEnable();
        // 检查身份
        if (userLoginDTO.getRoleId() != userSimpleVO.getRoleId()){
            log.error("身份信息错误,将抛出异常");
            throw new ServiceException(State.ERR_USER_ROLE_BAD,"身份选择错误,请重新选择");
        }
        // 检查用户状态
        if (isEnable!=1){
            log.error("管理员状态被禁用,将抛出异常");
            // 用户选择的管理员登陆
            if (userLoginDTO.getRoleId()==1){
                throw new ServiceException(State.ERR_USER_NOT_ENABLE,"管理员(" + username + ")已被禁用");
            }else if (userLoginDTO.getRoleId()==2){
                // 用户选择的员工登陆
                throw new ServiceException(State.ERR_USER_NOT_ENABLE,"员工(" + username + ")已被禁用");
            }
            // 用户选择的用户登陆
            throw new ServiceException(State.ERR_USER_NOT_ENABLE,"用户(" + username + ")已被禁用");
        }
        // 检查密码
        String password = userSimpleVO.getPassword();
        boolean matches = passwordEncoder.matches(userLoginDTO.getPassword(), password);
        // 对比失败   抛出异常
        if (!matches){
            log.error("管理员密码错误,将抛出异常!");
            throw new ServiceException(State.ERR_USER_PASSWORD_BAD,"密码错误");
        }
        log.trace("登录成功,返回用户数据:{}",userSimpleVO);
        // todo 修改user表中的last_login_ip(最后登录IP)   login_count(登录次数)  (最后登录时间)
        // 新建一个UserUpdateDTO 为更改用户信息做准备
        UserUpdateDTO userUpdateDTO = new UserUpdateDTO();
        // 以上这些从一个对象中把属性赋到另一个对象中,还可以使用:  (需要两个类中的属性名完全一致)
        UserDetailVO detailsByUsername = userMapper.getDetailsByUsername(username);
        BeanUtils.copyProperties(detailsByUsername,userUpdateDTO);
        log.debug("通过username查出来的UserDetailVO:{}",detailsByUsername);
        log.debug("赋值之后的userUpdateDTO:{}",userUpdateDTO);
        // 更改userUpdateDTO中的值
        userUpdateDTO.setLastLoginIp(userLoginDTO.getLastLoginIp());
        userUpdateDTO.setLoginCount(userUpdateDTO.getLoginCount()+1);
        userUpdateDTO.setGmtLastLogin(LocalDateTime.now());

        // 将用户表中的数据更新
        userMapper.updateUser(userUpdateDTO);


        // 对比成功,返回信息,返回前将不需要返回的数据设置为null
        userSimpleVO.setIsEnable(null);
        userSimpleVO.setPassword(null);
        return userSimpleVO;*/
    }

    @Override
    public UserSimpleVO parseJwt(String jwt) {
        // 程序能走到这里说明jwt是合法且没有过期的,所以不用考虑抛出异常的情况,直接解析后返回即可
        Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
        // 考虑到前端只需要用到用户名和roleId,所以只解析这两个属性即可(当初放置信息的时候也只放了两个)
        String username = claims.get("username").toString();

        String roleIdString = claims.get("roleId").toString();
        Long roleId = Long.valueOf(roleIdString.substring(15,roleIdString.length()-3));
        log.debug("获得roleId={}",claims.get("roleId").toString().substring(15,claims.get("roleId").toString().length()-3));
        // 创建一个UserSimpleVO对象,并将这两个元素放进去
        UserSimpleVO userSimpleVO = userMapper.getByUsername(username);

        return userSimpleVO;
    }

    @Override
    public List<UserDetailVO> listDetailsUser(Long roleId) {
        return userMapper.getListDetails(roleId);
    }

    @Override
    public UserDetailVO detailsUser(String username) {
        UserDetailVO userDetailVO = userMapper.getDetailsByUsername(username);
        if (userDetailVO == null){
            log.error("未查询到数据,抛出异常");
            throw new ServiceException(State.ERR_USER_NOT_FOUND,"用户不存在");
        }
        log.debug("查询成功,返回用户数据{}",userDetailVO);
        return userDetailVO;
    }

    @Override
    public void deleteUserById(Long id) {
        int row = userMapper.deleteUserById(id);
        if (row != 1){
            log.error("删除失败,将抛出异常");
            throw new ServiceException(State.ERR_DELETE,"删除用户失败,服务器忙,请稍后再次尝试!");
        }
    }


    @Override
    public void updateUser(UserUpdateDTO userUpdateDTO) {
        // 添加最后修改时间
        userUpdateDTO.setGmtModified(LocalDateTime.now());
        int row = userMapper.updateUser(userUpdateDTO);
        if (row != 1){
            log.error("修改用户信息失败,将抛出异常");
            throw new ServiceException(State.ERR_UPDATE,"修改用户信息失败,服务器忙,请稍后再次尝试!");
        }
    }


}
